/*
 * HexBase.java
 *
 * Created on November 15, 2007, 7:31 PM
 *
 * Copyright (C) 2007-2008  Hylke van der Schaaf
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, in version 3 of the License.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */
package hextd.hex;

import hextd.Constants;
import hextd.hex.HexTile.HighlightType;
import hextd.util.Geometry;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Shape;
import java.awt.Stroke;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.EnumMap;
import java.util.HashSet;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.usoog.tdcore.exception.NoSuchTileException;
import com.usoog.tdcore.exception.TileHasNoTowerException;
import com.usoog.tdcore.gamegrid.GameGrid;
import com.usoog.tdcore.gamegrid.GridPoint;
import com.usoog.tdcore.gamegrid.ScaledCanvas;
import com.usoog.tdcore.gamestate.GameState;
import com.usoog.tdcore.player.Player;
import com.usoog.tdcore.tile.Tile;
import com.usoog.tdcore.tower.Tower;

/**
 *
 * @author hylke
 */
public class HexBase extends HexTile {

	public static char key = 'b';
	protected char shortName;
	protected HexTile.HexType type;
	protected int x;
	protected int y;
	//
	protected double paintScale;
	protected float strokeScale;
	protected double offsetX, offsetY;
	protected double wPaint, hPaint;
	//
	private double[][] points;
	//
	protected Shape polyHex;
	protected Stroke stroke1;
	protected Stroke stroke2;
	protected Shape[] bordersE;
	protected static Color borderColor = Constants.colorBaseHexBorder;
	protected static Color borderEmptyColor = Constants.colorBaseEmptyHexBorder;
	protected static Color fillColor = new Color(Constants.colorBaseHexBackGround.getColorSpace(), Constants.colorBaseHexBackGround.getComponents(null), 0.5f);
	protected static Color highlightOK = new Color(Constants.colorHighlightOK.getColorSpace(), Constants.colorHighlightOK.getComponents(null), 0.5f);
	protected static Color highlightNOK = new Color(Constants.colorHighlightNOK.getColorSpace(), Constants.colorHighlightNOK.getComponents(null), 0.5f);
	private static Color rangeColor = Constants.colorRangeCircle;
	private Color highlightColor = highlightOK;
	//
	protected GameState gameState;
	protected ScaledCanvas canvas;
	protected HighlightType highlight = HighlightType.none;
	private float highlightRange = 0;
	private Shape rangeCircle;
	private boolean scaled = false;
	//
	private EnumMap<HexTile.Neighbour, Tile> neighbours = new EnumMap<Neighbour, Tile>(HexTile.Neighbour.class);
	//
	private boolean free = true;
	private int[] claims = new int[2];
	private int claimPlayer = -1;
	private Point2D.Double gameLocation, paintLocation;

	/** Creates a new instance of HexBase */
	public HexBase(GridPoint location, GameState gs, boolean buildable) {
		super(gs.getGameGrid());
		gameState = gs;
		setGridLocation(location);
		gameLocation = gameState.getGameGrid().getTileGameCoordinates(location, gameLocation);
		type = HexType.base;
		shortName = key;
		free = buildable;
	}

	@Override
	public void setHighlight(HighlightType highlight) {
		this.highlight = highlight;
		switch (this.highlight) {
			case placeTower:
				if (isBuildable(gameState.getGameManager().getLocalPlayer())) {
					highlightColor = highlightOK;
				} else {
					highlightColor = highlightNOK;
				}
				break;
			case editHex:
				highlightColor = highlightOK;
				break;
			case selectTower:
				highlightColor = highlightOK;
				break;
		}
	}

	@Override
	public void setHighlightRange(float range) {
		highlightRange = range;
		if (canvas != null) {
			double paintRange = highlightRange * paintScale;
			rangeCircle = new Ellipse2D.Double(paintLocation.x - paintRange, paintLocation.y - paintRange, paintRange * 2, paintRange * 2);
		}
	}

	@Override
	public void getNeighbours(int radius, Set<Tile> collection) {
		collection.add(this);
		if (radius > 0) {
			for (Tile neighbour : neighbours.values()) {
				if (neighbour != null) {
					neighbour.getNeighbours(radius - 1, collection);
				}
			}
		}
	}

	@Override
	public void initNeighbours() {
		GameGrid gameGrid = gameState.getGameGrid();
		GridPoint gridLoc = getGridLocation();
		try {
			neighbours.put(HexTile.Neighbour.right, gameGrid.getTile(HexTile.getNeightbourOf(gridLoc, Neighbour.rightDown)));
		} catch (NoSuchTileException ex) {
		}
		try {
			neighbours.put(HexTile.Neighbour.left, gameGrid.getTile(HexTile.getNeightbourOf(gridLoc, Neighbour.left)));
		} catch (NoSuchTileException ex) {
		}
		try {
			neighbours.put(HexTile.Neighbour.rightUp, gameGrid.getTile(HexTile.getNeightbourOf(gridLoc, Neighbour.rightUp)));
		} catch (NoSuchTileException ex) {
		}
		try {
			neighbours.put(HexTile.Neighbour.leftUp, gameGrid.getTile(HexTile.getNeightbourOf(gridLoc, Neighbour.leftUp)));
		} catch (NoSuchTileException ex) {
		}
		try {
			neighbours.put(HexTile.Neighbour.rightDown, gameGrid.getTile(HexTile.getNeightbourOf(gridLoc, Neighbour.rightDown)));
		} catch (NoSuchTileException ex) {
		}
		try {
			neighbours.put(HexTile.Neighbour.leftDown, gameGrid.getTile(HexTile.getNeightbourOf(gridLoc, Neighbour.leftDown)));
		} catch (NoSuchTileException ex) {
		}
	}

	@Override
	public void resetScale(ScaledCanvas sc) {
		super.resetScale(sc);
		canvas = sc;
		paintScale = sc.getPaintScale();
		strokeScale = sc.getDefaultStrokeWidth();
		offsetX = sc.getOffsetX();
		offsetY = sc.getOffsetY();
		paintLocation = super.getPaintLocation(paintLocation);

		points = Geometry.createHexCoords(paintScale, paintLocation.x, paintLocation.y, true);
		polyHex = Geometry.coordsToGeneralPath(points, true);

		ArrayList<Shape> borders = new ArrayList<Shape>();
		if (neighbours.get(HexTile.Neighbour.right) != null && neighbours.get(HexTile.Neighbour.right).getType().equals(HexType.path.name())) {
			// Right is empty
			Line2D tempLine = new Line2D.Double(points[1][0], points[1][1], points[2][0], points[2][1]);
			borders.add(tempLine);
		}
		if (neighbours.get(HexTile.Neighbour.rightUp) != null && neighbours.get(HexTile.Neighbour.rightUp).getType().equals(HexType.path.name())) {
			// Right-Up is empty
			Line2D tempLine = new Line2D.Double(points[0][0], points[0][1], points[1][0], points[1][1]);
			borders.add(tempLine);
		}
		if (neighbours.get(HexTile.Neighbour.rightDown) != null && neighbours.get(HexTile.Neighbour.rightDown).getType().equals(HexType.path.name())) {
			// Right-Down is empty
			Line2D tempLine = new Line2D.Double(points[2][0], points[2][1], points[3][0], points[3][1]);
			borders.add(tempLine);
		}
		if (neighbours.get(HexTile.Neighbour.left) != null && neighbours.get(HexTile.Neighbour.left).getType().equals(HexType.path.name())) {
			// Left is empty
			Line2D tempLine = new Line2D.Double(points[4][0], points[4][1], points[5][0], points[5][1]);
			borders.add(tempLine);
		}
		if (neighbours.get(HexTile.Neighbour.leftUp) != null && neighbours.get(HexTile.Neighbour.leftUp).getType().equals(HexType.path.name())) {
			// Left-Up is empty
			Line2D tempLine = new Line2D.Double(points[5][0], points[5][1], points[0][0], points[0][1]);
			borders.add(tempLine);
		}
		if (neighbours.get(HexTile.Neighbour.leftDown) != null && neighbours.get(HexTile.Neighbour.leftDown).getType().equals(HexType.path.name())) {
			// Left-Down is empty
			Line2D tempLine = new Line2D.Double(points[3][0], points[3][1], points[4][0], points[4][1]);
			borders.add(tempLine);
		}

		bordersE = new Shape[borders.size()];
		bordersE = borders.toArray(bordersE);

		stroke1 = new BasicStroke(1.0f * strokeScale, BasicStroke.CAP_ROUND, BasicStroke.JOIN_BEVEL);
		stroke2 = new BasicStroke(3.0f * strokeScale, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);

		double paintRange = highlightRange * paintScale;
		rangeCircle = new Ellipse2D.Double(paintLocation.x - paintRange, paintLocation.y - paintRange, paintRange * 2, paintRange * 2);
		scaled = true;
	}

	public void paintRangeCircle(Graphics2D g2) {
		g2.setColor(rangeColor);
		g2.draw(rangeCircle);
	}

	@Override
	public void paint(Graphics2D g2, int gameTime) {
		if (g2 != null && scaled) {
			Stroke defaultStroke = g2.getStroke();

			g2.setStroke(stroke1);
			g2.setColor(fillColor);
			if (claimPlayer >= 0) {
				g2.setColor(Constants.getColorPlayerAlpha(claimPlayer));
			}
			g2.fill(polyHex);
			g2.setColor(borderColor);
			g2.draw(polyHex);

			g2.setStroke(stroke2);
			g2.setColor(borderEmptyColor);
			for (int i = 0; i < bordersE.length; i++) {
				g2.draw(bordersE[i]);
			}

			g2.setStroke(defaultStroke);
		}
	}

	@Override
	public void paintEffects(Graphics2D g2, int gameTime) {
		if (this.scaled) {
			switch (this.highlight) {
				case placeTower:
					g2.setColor(highlightColor);
					paintRangeCircle(g2);
					g2.fill(polyHex);
					break;
				case editHex:
					g2.setColor(highlightColor);
					g2.fill(this.polyHex);
					break;
				case selectTower:
					g2.setColor(highlightColor);
					g2.fill(this.polyHex);
					break;
			}
		}
	}

	@Override
	public char getShortName() {
		return shortName;
	}

	@Override
	public String getType() {
		return type.name();
	}

	@Override
	public boolean isBuildable(Player player) {
		if (free) {
			if (player.getId() == claimPlayer) {
				return true;
			}
		}
		return false;
	}

	@Override
	public void addClaim(Player p, int level) {
		int id = p.getId();
		Tower t = null;
		if (id >= 0) {
			if (id >= claims.length) {
				int[] newClaims = new int[id + 1];
				System.arraycopy(claims, 0, newClaims, 0, claims.length);
				claims = newClaims;
			}
			claims[id] += level;
			if (hasTower()) {
				try {
					t = getTower();
				} catch (TileHasNoTowerException ex) {
					Logger.getLogger(HexBase.class.getName()).log(Level.SEVERE, null, ex);
				}
			}
			if (t != null) {
				claimPlayer = t.getOwner().getId();
			} else {
				int highest = 0;
				claimPlayer = -1;
				for (int i = 0; i < claims.length; i++) {
					if (claims[i] > highest) {
						highest = claims[i];
						claimPlayer = i;
					} else if (claims[i] == highest) {
						claimPlayer = -1;
					}
				}
				if (t != null && !t.isActive()) {
					if (claimPlayer != t.getOwner().getId()) {
						System.out.println("HexBase::addClaim: Hex (" + x + "," + y + ") claimed by player " + claimPlayer
								+ " before tower of player " + t.getOwner() + " activated. Cancelling build.");
						t.cancelBuild();
					}
				}
			}
			gameState.getGameGrid().tileChanged();
		}
	}

	@Override
	public void addClaim(Player player, int level, int radius) {
		Set<Tile> hexes = new HashSet<Tile>(19);
		getNeighbours(radius, hexes);
		for (Tile hex : hexes) {
			((HexTile) hex).addClaim(player, level);
		}
	}
}
